/*
*    Reversi Contender homepage:  http://code.google.com/p/reversi-contender
*
*    copyright 2011 steven sims <steven.joseph.sims at gmail.com>
*
*    This file is part of Reversi Contender.
*
*    Reversi Contender is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    Reversi Contender is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with Reversi Contender.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.threadswarm.reversi.gui;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import org.threadswarm.reversi.BoardChangeListener;
import org.threadswarm.reversi.Cell;
import org.threadswarm.reversi.GameSession;
import org.threadswarm.reversi.ReversiGame;
import org.threadswarm.reversi.Status;
import org.threadswarm.reversi.gui.animation.AlphaSettable;

/**
 *
 * @author steve
 */
public class CellComponent extends JComponent implements BoardChangeListener, AlphaSettable {

    private final static int COMP_WIDTH = 48;
    private final static int COMP_HEIGHT = 48;
    private final static Map<Status, CellColor> COLOR_MAP = new EnumMap<Status, CellColor>(Status.class);
    private final static Color BG_COLOR = new Color(0, 100, 0);
    private final Point cellCoordinates;
    private final ReversiGame game;
    private Cell cell;
    private boolean insertable = false;
    private CellColor cellColor;
    private float availInsertionAlpha = 0.7f;

    static {
        COLOR_MAP.put(Status.BLACK, CellColor.DARK);
        COLOR_MAP.put(Status.WHITE, CellColor.LIGHT);
        COLOR_MAP.put(Status.EMPTY, CellColor.EMPTY);
    }

    public CellComponent(Point cellCoordinates, ReversiGame game) {
        this.cellCoordinates = cellCoordinates;
        this.game = game;
        cell = game.getCellCopy(cellCoordinates);
        cellColor = COLOR_MAP.get(cell.getStatus());
        Dimension dimension = new Dimension(48, 48);
        this.setPreferredSize(dimension);
        this.setMaximumSize(dimension);
        this.setMinimumSize(dimension);
    }

    public void refresh() {
        GameSession prefs = GameSession.getInstance();
        cell = game.getCellCopy(cellCoordinates);

        if(cell.getStatus() == Status.EMPTY && insertable && prefs.isMarkAvailable()){
            cellColor = COLOR_MAP.get(game.getCurrentStatus());
        }else{
            cellColor = COLOR_MAP.get(cell.getStatus());
        }

        repaint(0, 13, 13, 24, 24);  //repaint only the screen area where the circle lies
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics gTmp = g;  //Graphics object not used elsewhere so no worry of clobering
        Rectangle clip = gTmp.getClipBounds();

        gTmp.setColor(BG_COLOR);
        gTmp.fillRect(clip.x, clip.y, clip.width, clip.height);
        if(clip.x == 0 || clip.y == 0){
            gTmp.setColor(Color.BLACK);
            gTmp.drawRect(0, 0, 48, 48);
        }

        if(cellColor.color != null){  
            Graphics2D g2 = (Graphics2D) gTmp;
            if(insertable)            
                g2.setComposite(AlphaComposite.SrcOver.derive(availInsertionAlpha));
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setColor(cellColor.color);
            g2.fillOval(13, 13, 24, 24);
        }
    }

    public void setInsertable(boolean insertable) {
        setInsertable(insertable, false);
    }
    
    public void setInsertable(boolean insertable, boolean animationEnabled) {
        this.insertable = insertable;
        if(!animationEnabled){
            availInsertionAlpha = 0.2f;
            refresh();
        }
    }

    public boolean isInsertable() {
        return insertable;
    }

    public void insert(final Status status) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                game.insert(cell.getPoint(), status);
            }
        });
        insertable = false;
    }

    public Point getCellCoordinates() {
        return cellCoordinates;
    }

    @Override
    public int getWidth() {
        return COMP_WIDTH;
    }

    @Override
    public int getHeight() {
        return COMP_HEIGHT;
    }

    @Override
    public void boardChangeEvent() {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                refresh();
            }
        });
    }

    @Override
    public void setAlpha(float alpha) {
        availInsertionAlpha = (alpha < 0.4f)? alpha : 0.4f;
    }

    private static enum CellColor {
        DARK(Color.BLACK), LIGHT(Color.WHITE), INSERTABLE(new Color(0, 128, 0)), EMPTY(null);

        public final Color color;

        private CellColor(Color color) {
            this.color = color;
        }
    }

}
